package org.example.learn.asm;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test extends ClassLoader {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
//        System.out.println("123");
        byte[] bytes = generate();
//        outputClazz(bytes);
        Class<?>  clazz = new Test().defineClass("org.example.learn.asm.TestSum",bytes,0,bytes.length);

        Method method = clazz.getMethod("sum",int.class,int.class);
        Object val = method.invoke(clazz.newInstance(),8,9);
        System.out.println(val);
    }

    public static byte[] generate(){
        ClassWriter classWriter = new ClassWriter(0);

        // <init> 无参构造函数
        {
            MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC,"<init>","()V",null,null);
            methodVisitor.visitVarInsn(Opcodes.ALOAD,0);
            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL,"java/lang/Object","<init>","()V",false);
            methodVisitor.visitInsn(Opcodes.RETURN);
            methodVisitor.visitMaxs(1,1);
            methodVisitor.visitEnd();
        }

        {
            classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "org/example/learn/asm/TestSum", null, "java/lang/Object", null);

            MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "sum", "(II)I", null, null);

            methodVisitor.visitVarInsn(Opcodes.ILOAD, 1);
            methodVisitor.visitVarInsn(Opcodes.ILOAD, 2);
            methodVisitor.visitInsn(Opcodes.IADD);

            methodVisitor.visitInsn(Opcodes.IRETURN);

            methodVisitor.visitMaxs(2, 3);
            methodVisitor.visitEnd();
        }

        classWriter.visitEnd();

        return classWriter.toByteArray();
    }

    public static void outputClazz(byte[] bytes){
        FileOutputStream out = null;
        String path = Test.class.getResource("/").getPath() + "TestSum.class";
        try {
            out = new FileOutputStream(path);
            out.write(bytes);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (null != out) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
